/*************************************************************************************
**																					**
**	openrf_mac.c			OpenRF(tm) MAC Layer   									**
** 																					**
**************************************************************************************
**																					**
** Written By:	Steve Montgomery													**
**				Digital Six Laboratories LLC										**
** (c)2012, 2013, 2014 Digital Six Labs, All rights reserved						**
**																					**
**************************************************************************************/
/*! \mainpage This is the main page
 *
 * \section intro_sec Introduction
 *
 * OpenRF™ is an open source embedded wireless networking protocol platform.   Using the MIT
 * License, OpenRF™ is not only free to use, but there are no commercial restrictions on its use.
 *
 * \section cod_doc Code documentation
 *
 * \ref Microcontroller Documentation for the RL78 based microcontroller API
 *
 * \ref Radio Documentation for the SX1231 based radio API
 *
 * \ref OpenRF Documentation for the OpenRF API
 *
 * \ref Utilities Documentation for the micro specific Utilities provided with OpenRF
 */
//
// Revision History
//
// Revision		Date	Revisor		Description
// ===================================================================================
// ===================================================================================

#include "openrf_mac.h"
#include <stdlib.h>

enum
{
	kAckTimer,
	kSyncTimer,
	kLockTimer
} timerDefs;
// ***********************************************************************************
// ** Private variables
// ***********************************************************************************
struct
{
	tOpenRFStates macState;
	tPacketTypes txPacketType;
	tPacketTypes rxPacketType;
	U16 timers[4];
	U16 ackTimeout;
	U8 ackRetries;
	U8 ackRetryCounter;
	UU32 rxSourceMAC;
	UU32 rxDestinationMAC;
	UU32 macAddress;
	U8 operatingMode;
	U8 isLocked;
	U16 frameLength;
#ifdef RADIOMODE_LORA
	U8 gfskEnabled;
	UU32 networkId;
	U8 alreadyHopped;
#endif
	U16 listenPeriod;
	tReceiveModes listenMode;
	U8 transmitPacket[64];
	U8 receivePacket[64];
	U8 txPacketLength;
	U8 rxPacketLength;
	S8 packetRSSI;
	U8 packetSNR;
	UU32 txDestinationMAC;
	UU16 preambleCount;
}  openRFPrivateData;

extern UU32 _RTCDateTimeInSecs;

// ***********************************************************************************
// ** Event Handlers 
// ***********************************************************************************
void TerminateReceive()
{
	switch(openRFPrivateData.listenMode)
	{
	case kBlockingCADSingle:
	case kBlockingNoCADSingle:
		openRFPrivateData.macState = kIdle;
		NotifyMacReceiveError();
		break;
	case kBlockingCADContinuous:
	case kBlockingNoCADContinuous:
		break;
	case kEventingCADSingle:
	case kEventingNoCADSingle:
		openRFPrivateData.macState = kIdle;
		NotifyMacReceiveError();
		break;
	case kEventingCADContinuous:
	case kEventingNoCADContinuous:
		break;
	}
}
// called by RadioAPI when a packet is received.
extern void NotifyRadioPacketReceived(U8 length, U8 *SDU, S8 rssi)
{
	UU32 sourceMACAddress, destMACAddress;
	UU32 timeStamp;
	U8 *pktPtr,i;

	pktPtr = SDU;
	// packet structure
	// [pktType:8][destMAC:32][srcMAC:32][len:8][payload:8*length]
	openRFPrivateData.rxPacketType = *pktPtr++;
	openRFPrivateData.packetRSSI = rssi;
	openRFPrivateData.rxDestinationMAC.U8[3] = *pktPtr++;
	openRFPrivateData.rxDestinationMAC.U8[2] = *pktPtr++;
	openRFPrivateData.rxDestinationMAC.U8[1] = *pktPtr++;
	openRFPrivateData.rxDestinationMAC.U8[0] = *pktPtr++;
	if( (openRFPrivateData.rxPacketType == kUniAckPacketType) || (openRFPrivateData.rxPacketType == kAckPacketType ) )
	{

		openRFPrivateData.rxSourceMAC.U8[3] = *pktPtr++;
		openRFPrivateData.rxSourceMAC.U8[2] = *pktPtr++;
		openRFPrivateData.rxSourceMAC.U8[1] = *pktPtr++;
		openRFPrivateData.rxSourceMAC.U8[0] = *pktPtr++;
	}
	openRFPrivateData.rxPacketLength = *pktPtr++;
	for(i=0;i<openRFPrivateData.rxPacketLength;i++)
	{
		openRFPrivateData.receivePacket[i] = *pktPtr++;
	}

	// now that we have the full packet, lets see what logic we need to execute on its behalf.  But only on packets that
	// are directed at us.
	if (openRFPrivateData.rxPacketType == kMulticastPacketType)
	{
		NotifyMacPacketReceived(openRFPrivateData.rxPacketType,openRFPrivateData.rxSourceMAC,openRFPrivateData.rxPacketLength,&openRFPrivateData.receivePacket[0],openRFPrivateData.packetRSSI);
		TerminateReceive();
	}
	else if(openRFPrivateData.rxDestinationMAC.U32 == openRFPrivateData.macAddress.U32)
	{
		if(openRFPrivateData.rxPacketType == kUniAckPacketType)
		{
			// todo: test receiving of UniAckPacketType

			// If the packet is a UniAck packet, then we need to send an acknowledgement.
			openRFPrivateData.macState = kSendAck;
			NotifyMacPacketReceived(openRFPrivateData.rxPacketType,openRFPrivateData.rxSourceMAC,openRFPrivateData.rxPacketLength,&openRFPrivateData.receivePacket[0],openRFPrivateData.packetRSSI);
		}
		else if (openRFPrivateData.rxPacketType == kAckPacketType)
		{

			// if it was an ack, then we need to make sure it was for the last packet we sent.
			if( (openRFPrivateData.txPacketType == kUniAckPacketType) && (openRFPrivateData.txDestinationMAC.U32 == openRFPrivateData.rxSourceMAC.U32))
			{
				// We got an ack for the last packet we received, so we need to update our MAC state and notify the application
				openRFPrivateData.macState = kPacketTransmitted;
				NotifyMacPacketSent();
			}
			else
			{
				TerminateReceive();
			}
		}
		else if(openRFPrivateData.rxPacketType == kUniNoAckPacketType)
		{
			NotifyMacPacketReceived(openRFPrivateData.rxPacketType,openRFPrivateData.rxSourceMAC,openRFPrivateData.rxPacketLength,&openRFPrivateData.receivePacket[0],openRFPrivateData.packetRSSI);
			TerminateReceive();
		}
	}
	else
	{
		TerminateReceive();
	}
}
// called by RadioAPI when an error occurs while in receive mode
extern void NotifyRadioReceiveError()
{
	switch(openRFPrivateData.listenMode)
	{
	case kBlockingCADSingle:
	case kBlockingNoCADSingle:
	case kEventingCADSingle:
	case kEventingNoCADSingle:
		//break;
	case kBlockingCADContinuous:
	case kBlockingNoCADContinuous:
	case kEventingCADContinuous:
	case kEventingNoCADContinuous:
		openRFPrivateData.macState = kIdle;
		RadioSleepMode();
		NotifyMacReceiveError();
		break;
	}

}
// called by radioAPI once a packet has been fully sent
extern void NotifyRadioPacketSent()
{

	if(openRFPrivateData.txPacketType == kUniAckPacketType)
		openRFPrivateData.macState = kAckRequired;
	else if(openRFPrivateData.txPacketType == kAckPacketType)
	{
		openRFPrivateData.macState = kIdle;
	}
	else
	{
		openRFPrivateData.macState = kIdle;
		NotifyMacPacketSent();
	}
}
// called by RadioAPI when a packet send error happens
extern void NotifyRadioPacketSendError()
{
	NotifyMacPacketSendError(kUndefined);
}
extern void NotifyRadio1Second()
{
	NotifyMac1Second();
}
extern void NotifyRadio1MilliSecond()
{
	U8 i;
	NotifyMac1MilliSecond();
	for(i=0;i<4;i++)
		openRFPrivateData.timers[i]++;

		
}
// ***********************************************************************************
// ** Internal functions
// ***********************************************************************************
void ClearOpenRFTimer(U8 timerNumber)
{
	DisableInterrupts;
	openRFPrivateData.timers[timerNumber] = 0;
	EnableInterrupts;
}

// ***********************************************************************************
// ** Public API
// ***********************************************************************************
U16 timeLeft;
U16 timeRequired;
U16 lockTime;

void OpenRFSendPacket(UU32 destAddress, tPacketTypes packetType, U8 length, U8 *txBuffer)
{
	U8 *bufPtr,i;
	// send the packet and do not block until complete.

	// packet structure
	// [pktType:8][destMAC:32][srcMAC:32][len:8][payload:8*length]
	bufPtr = &openRFPrivateData.transmitPacket[0];
	*bufPtr++=(U8)packetType;
	*bufPtr++=destAddress.U8[3];
	*bufPtr++=destAddress.U8[2];
	*bufPtr++=destAddress.U8[1];
	*bufPtr++=destAddress.U8[0];
	if(packetType==kUniAckPacketType)
	{
		// todo: test UniAckPacket transmission
		*bufPtr++=openRFPrivateData.macAddress.U8[3];
		*bufPtr++=openRFPrivateData.macAddress.U8[2];
		*bufPtr++=openRFPrivateData.macAddress.U8[1];
		*bufPtr++=openRFPrivateData.macAddress.U8[0];
		openRFPrivateData.ackRetryCounter;
	}
	*bufPtr++=length;
	for(i=0;i<length;i++)
		*bufPtr++=*txBuffer++;
	openRFPrivateData.txPacketType = packetType;
	openRFPrivateData.txPacketLength = bufPtr - &openRFPrivateData.transmitPacket[0];
	openRFPrivateData.txDestinationMAC.U32 = destAddress.U32;
	openRFPrivateData.macState = kTransmitting;
	RadioSetPreambleCount(openRFPrivateData.preambleCount);
	RadioSendPacket(openRFPrivateData.txPacketLength,&openRFPrivateData.transmitPacket[0], kEventDriven);
}
void OpenRFSetPreamble(U16 preambleCount)
{
	openRFPrivateData.preambleCount.U16 = preambleCount;
}
void OpenRFInitialize(tOpenRFInitializer ini)
{
#ifdef RADIOMODE_LORA
	RadioInitializeAPI();
#else
	// initialization for the FSK mode
	tRadioInitialization rini;
	//ResetRadio();
	//X69
	openRFPrivateData.gfskEnabled = ini.GfskModifier;

	rini.HopTable = 0;
	rini.MacAddress = ini.MacAddress;
	rini.NetworkId = ini.NetworkId;
	rini.GausianEnabled = ini.GfskModifier;
	rini.FhssStepSize = 100;

	RadioInitialize(rini);
	//openRFPrivateData.macAddress.U32 = macAddress.U32;
	//SetMACAddress(macAddress);
	RadioSetDataRate(ini.DataRate);
	RadioSetEncryptionKey(&ini.EncryptionKey.U8[0],16);
#endif
	RadioSetEncryptionKey(ini.EncryptionKey);
	openRFPrivateData.macAddress = ini.MacAddress;
	openRFPrivateData.ackTimeout = ini.AckTimeout;
	openRFPrivateData.macState = kIdle;
	openRFPrivateData.ackRetries = ini.AckRetries;
	openRFPrivateData.alreadyHopped = 0;
	openRFPrivateData.txPacketType = kNone;
	openRFPrivateData.rxPacketType = kNone;
	EnableIntP0();
}

void OpenRFListenForPacket(tReceiveModes mode, U16 period)
{
	openRFPrivateData.macState = kListening;
	openRFPrivateData.listenMode = mode;
	openRFPrivateData.listenPeriod = period;

	openRFPrivateData.timers[3]=0;

	RadioSetReceiveTimeout(period);
	RadioReceivePacket(mode);
}
tOpenRFStates OpenRFLoop()
{
	U16 i;
	// todo: OpenRFLoop() OpenRF
	//U8 oState = GetRadioState();
	switch(openRFPrivateData.macState)
	{
	case kIdle:
		break;
	case kTransmitting:
		break;
	case kSendingAck:
		break;
	case kListening:
		// if we are in continuous listen mode, we need to listen until our timer expires.  then we put the radio to sleep and
		// set the mac state to idle.
		/*if(openRFPrivateData.listenMode>=kEventingCADContinuous)
		{
			// only manage the timer if the listen period is non zero.  If it is zero, the listen period is indefinite.
			if(openRFPrivateData.listenPeriod>0)
				if(openRFPrivateData.timers[3]>openRFPrivateData.listenPeriod)
				{
					RadioSleepMode();
					openRFPrivateData.macState = kIdle;
				}
		}*/
		break;
	case kWaitingForAck:
		if(openRFPrivateData.timers[kAckTimer]>=openRFPrivateData.ackTimeout)
		{
			openRFPrivateData.ackRetryCounter++;
			if(openRFPrivateData.ackRetryCounter<openRFPrivateData.ackRetries)
			{
				RadioSendPacket(openRFPrivateData.txPacketLength, &openRFPrivateData.transmitPacket[0],kEventDriven);
				openRFPrivateData.macState = kTransmitting;
			}
			else
			{
				RadioSleepMode();
				NotifyMacPacketSendError(kNoAck);
			}

		}
		break;
	case kSendAck:
		// send an ack to the guy we got the message from
		OpenRFSendPacket(openRFPrivateData.rxSourceMAC, kAckPacketType,0,0);
		openRFPrivateData.macState = kSendingAck;
		break;
	case kAckRequired:
		RadioReceivePacket(kEventingNoCADContinuous);
		openRFPrivateData.macState = kWaitingForAck;
		ClearTimer(kAckTimer);
		break;
	case kReceiving:
		break;
	case kPacketReceived:
		break;
	case kPacketTransmitted:
		break;
	case kRadioError:
		break;
	}
	return openRFPrivateData.macState;
}
U8 OpenRFReadyToSend()
{
	return RadioIsIdle();
}
void OpenRFSleep(U8 level)
{
	openRFPrivateData.macState = kIdle;
	RadioSleepMode();
}
